﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Collections.Generic;
using System.Windows.Automation.Peers;
using System.Windows.Automation;
using System.Windows.Automation.Provider;

namespace SLControlLibrary
{
    public enum ScrollAmountCombined
    {
        Large,
        Small,
        NoAmount,
    }

    public class ScrollInfo
    {
        public double HorizontalScrollPercent { get; set; }
        public double VerticalScrollPercent { get; set; }
        public ScrollAmount HorizontalIncrementScrollAmount { get; set; }
        public ScrollAmount HorizontalDecrementScrollAmount { get; set; }
        public ScrollAmount VerticalIncrementScrollAmount { get; set; }
        public ScrollAmount VerticalDecrementScrollAmount { get; set; }

        public ScrollInfo()
        {

        }

        public ScrollInfo(double horizontalScrollPercent, double verticalScrollPercent, ScrollAmount horizontalIncrementScrollAmount, ScrollAmount horizontalDecrementScrollAmount, ScrollAmount verticalIncrementScrollAmount, ScrollAmount verticalDecrementScrollAmount)
        {
            HorizontalScrollPercent = horizontalScrollPercent;
            VerticalScrollPercent = verticalScrollPercent;
            HorizontalIncrementScrollAmount = horizontalIncrementScrollAmount;
            HorizontalDecrementScrollAmount = horizontalDecrementScrollAmount;
            VerticalIncrementScrollAmount = verticalIncrementScrollAmount;
            VerticalDecrementScrollAmount = verticalDecrementScrollAmount;
        }
    }

    public static class MouseWheel
    {
        /// <summary>
        /// the current mouse position
        /// </summary>
        private static Point currentPoint;
        private static UIElement s_rootElement;
        private static Dictionary<UIElement, ScrollInfo> scrollInfoTable;

        static MouseWheel()
        {
            // register events (for differet browsers)
            HtmlPage.Window.AttachEvent("DOMMouseScroll", MouseWheelTurned);
            HtmlPage.Window.AttachEvent("onmousewheel", MouseWheelTurned);
            HtmlPage.Document.AttachEvent("onmousewheel", MouseWheelTurned);

            scrollInfoTable = new Dictionary<UIElement, ScrollInfo>();
        }

        public static double GetHorizontalScrollPercent(this UIElement element)
        {
            if (scrollInfoTable.ContainsKey(element))
            {
                return scrollInfoTable[element].HorizontalScrollPercent;
            }
            else
            {
                return 0;
            }
        }

        public static double GetVerticalScrollPercent(this UIElement element)
        {
            if (scrollInfoTable.ContainsKey(element))
            {
                return scrollInfoTable[element].VerticalScrollPercent;
            }
            else
            {
                return 0;
            }
        }

        public static ScrollAmount GetHorizontalIncrementScrollAmount(this UIElement element)
        {
            if (scrollInfoTable.ContainsKey(element))
            {
                return scrollInfoTable[element].HorizontalIncrementScrollAmount;
            }
            else
            {
                return ScrollAmount.NoAmount;
            }
        }

        public static ScrollAmount GetHorizontalDecrementScrollAmount(this UIElement element)
        {
            if (scrollInfoTable.ContainsKey(element))
            {
                return scrollInfoTable[element].HorizontalDecrementScrollAmount;
            }
            else
            {
                return ScrollAmount.NoAmount;
            }
        }

        public static ScrollAmount GetVerticalIncrementScrollAmount(this UIElement element)
        {
            if (scrollInfoTable.ContainsKey(element))
            {
                return scrollInfoTable[element].VerticalIncrementScrollAmount;
            }
            else
            {
                return ScrollAmount.NoAmount;
            }
        }

        public static ScrollAmount GetVerticalDecrementScrollAmount(this UIElement element)
        {
            if (scrollInfoTable.ContainsKey(element))
            {
                return scrollInfoTable[element].VerticalDecrementScrollAmount;
            }
            else
            {
                return ScrollAmount.NoAmount;
            }
        }

        public static ScrollInfo GetScrollInfo(this UIElement element)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo(0, 0, ScrollAmount.LargeIncrement, ScrollAmount.LargeDecrement, ScrollAmount.LargeIncrement, ScrollAmount.LargeDecrement));
            }

            return scrollInfoTable[element];
        }

        public static void SetHorizontalScrollPercent(this UIElement element, double percent)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            scrollInfoTable[element].HorizontalScrollPercent = percent;
        }

        public static void SetVerticalScrollPercent(this UIElement element, double percent)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            scrollInfoTable[element].VerticalScrollPercent = percent;
        }

        public static void SetHorizontalScrollAmount(this UIElement element, ScrollAmountCombined scrollAmount)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            switch (scrollAmount)
            {
                case ScrollAmountCombined.Large:

                    scrollInfoTable[element].HorizontalIncrementScrollAmount = ScrollAmount.LargeIncrement;
                    scrollInfoTable[element].HorizontalDecrementScrollAmount = ScrollAmount.LargeDecrement;
                    break;

                case ScrollAmountCombined.Small:

                    scrollInfoTable[element].HorizontalIncrementScrollAmount = ScrollAmount.SmallIncrement;
                    scrollInfoTable[element].HorizontalDecrementScrollAmount = ScrollAmount.SmallDecrement;
                    break;

                case ScrollAmountCombined.NoAmount:

                    scrollInfoTable[element].HorizontalIncrementScrollAmount = ScrollAmount.NoAmount;
                    scrollInfoTable[element].HorizontalDecrementScrollAmount = ScrollAmount.NoAmount;
                    break;
            }
        }

        public static void SetHorizontalIncrementScrollAmount(this UIElement element, ScrollAmount scrollAmount)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            scrollInfoTable[element].HorizontalIncrementScrollAmount = scrollAmount;
        }

        public static void SetHorizontalDecrementScrollAmount(this UIElement element, ScrollAmount scrollAmount)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            scrollInfoTable[element].HorizontalDecrementScrollAmount = scrollAmount;
        }

        public static void SetVerticalScrollAmount(this UIElement element, ScrollAmountCombined scrollAmount)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            switch (scrollAmount)
            {
                case ScrollAmountCombined.Large:

                    scrollInfoTable[element].VerticalIncrementScrollAmount = ScrollAmount.LargeIncrement;
                    scrollInfoTable[element].VerticalDecrementScrollAmount = ScrollAmount.LargeDecrement;
                    break;

                case ScrollAmountCombined.Small:

                    scrollInfoTable[element].VerticalIncrementScrollAmount = ScrollAmount.SmallIncrement;
                    scrollInfoTable[element].VerticalDecrementScrollAmount = ScrollAmount.SmallDecrement;
                    break;

                case ScrollAmountCombined.NoAmount:

                    scrollInfoTable[element].VerticalIncrementScrollAmount = ScrollAmount.NoAmount;
                    scrollInfoTable[element].VerticalDecrementScrollAmount = ScrollAmount.NoAmount;
                    break;
            }
        }

        public static void SetVerticalIncrementScrollAmount(this UIElement element, ScrollAmount scrollAmount)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            scrollInfoTable[element].VerticalIncrementScrollAmount = scrollAmount;
        }

        public static void SetVerticalDecrementScrollAmount(this UIElement element, ScrollAmount scrollAmount)
        {
            if (!scrollInfoTable.ContainsKey(element))
            {
                scrollInfoTable.Add(element, new ScrollInfo());
            }

            scrollInfoTable[element].VerticalDecrementScrollAmount = scrollAmount;
        }

        public static void Enable(UIElement rootElement)
        {
            // prevent null reference exception
            if (rootElement == null)
                return;

            // only one root element possible with this implementation
            if (s_rootElement != null)
                s_rootElement.MouseMove -= new MouseEventHandler(rootElement_MouseMove);

            s_rootElement = rootElement;
            s_rootElement.MouseMove += new MouseEventHandler(rootElement_MouseMove);

        }

        static void rootElement_MouseMove(object sender, MouseEventArgs e)
        {
            currentPoint = e.GetPosition(null);
        }

        private static IScrollProvider GetScrollProvider(this UIElement element)
        {
            // get automation peer (if already created for this control)
            var automationPeer = FrameworkElementAutomationPeer.FromElement(element);

            if (automationPeer == null)
            {
                // create automation peer for element
                automationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(element);
            }

            //expect null: some elements doesn't have an automation peer implemented
            if (automationPeer != null)
            {
                // horizontal scrolling?
                bool ctrlKey = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;

                // try to get scroll provider
                // note: TextBoxAutomationPeer does not implement IScrollProvider

                var scrollProvider = automationPeer.GetPattern(PatternInterface.Scroll) as IScrollProvider;

                return scrollProvider;
            }

            return null;
        }

        private static void MouseWheelTurned(Object sender, HtmlEventArgs args)
        {
            double delta = 0;
            ScriptObject e = args.EventObject;

            if (e.GetProperty("wheelDelta") != null) // IE and Opera
            {
                delta = ((double)e.GetProperty("wheelDelta"));
                if (HtmlPage.Window.GetProperty("opera") != null)
                    delta = -delta;
            }
            else if (e.GetProperty("detail") != null) // Mozilla and Safari
            {
                delta = -((double)e.GetProperty("detail"));
            }

            if (delta != 0)
            {
                args.PreventDefault();
                e.SetProperty("returnValue", false);

                // go through all element beneath the current mouse position
                IEnumerable<UIElement> elements = VisualTreeHelper.FindElementsInHostCoordinates(currentPoint, s_rootElement);
                foreach (UIElement element in elements)
                {
                    // get automation peer (if already created for this control)
                    var automationPeer = FrameworkElementAutomationPeer.FromElement(element);

                    if (automationPeer == null)
                    {
                        // create automation peer for element
                        automationPeer = FrameworkElementAutomationPeer.CreatePeerForElement(element);
                    }

                    //expect null: some elements doesn't have an automation peer implemented
                    if (automationPeer != null)
                    {
                        // horizontal scrolling?
                        bool ctrlKey = (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control;

                        // try to get scroll provider
                        // note: TextBoxAutomationPeer does not implement IScrollProvider

                        var scrollProvider = automationPeer.GetPattern(PatternInterface.Scroll) as IScrollProvider;

                        if (scrollProvider != null)
                        {
                            // set scroll amount

                            var scrollInfo = element.GetScrollInfo();
                            var scrollAmount = ScrollAmount.NoAmount;

                            scrollProvider.SetScrollPercent(scrollInfo.HorizontalScrollPercent, scrollInfo.VerticalScrollPercent);

                            // is scrolling horizontal possible

                            if (scrollProvider.HorizontallyScrollable && ctrlKey)
                            {
                                if (delta < 0)
                                {
                                    scrollAmount = scrollInfo.HorizontalIncrementScrollAmount;
                                }
                                else if (delta > 0)
                                {
                                    scrollAmount = scrollInfo.HorizontalDecrementScrollAmount;
                                }

                                scrollProvider.Scroll(scrollAmount, System.Windows.Automation.ScrollAmount.NoAmount);

                                // break the further search in the uielement collection
                                break; // foreach
                            }
                            else if (scrollProvider.VerticallyScrollable)
                            {
                                if (delta < 0)
                                {
                                    scrollAmount = scrollInfo.VerticalIncrementScrollAmount;
                                }
                                else if (delta > 0)
                                {
                                    scrollAmount = scrollInfo.VerticalDecrementScrollAmount;
                                }

                                scrollProvider.Scroll(System.Windows.Automation.ScrollAmount.NoAmount, scrollAmount);

                                // break the further search in the uielement collection
                                break; // foreach
                            }

                            // don't break here, because of encapsulated scroll viewers such as in the treeview from the sl-toolkit
                            //break;
                        }
                    }
                }
            }
        }
    }
}
